package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-28
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {
    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;
    private final LearningRecordDelayTaskHandler taskHandler;
    private final RabbitMqHelper mqHelper;

    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.查询课程学习表获取最近学习小节id
        LearningLesson lesson = lessonService.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null){
            throw new BizIllegalException("当前课程并未加入课程表");
        }
        //3.查询学习记录表获取学习记录
        List<LearningRecord> list = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .list();
        /*if (CollUtils.isEmpty(list)){
            throw new BizIllegalException("当前课程并未学习过");
        }*/
        //4.封装dto传递给其他微服务
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        List<LearningRecordDTO> dtos = BeanUtil.copyToList(list, LearningRecordDTO.class);
        dto.setRecords(dtos);
        return dto;
    }

    /**
     * 第一次学完后的操作
    * */
    @Override
    @Transactional
    public void commitLearningRecord(LearningRecordFormDTO recordDTO) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.处理学习记录
        boolean finished = false;
        if (recordDTO.getSectionType() == SectionType.VIDEO) {
            // 2.1.处理视频
            finished = handleVideoRecord(userId, recordDTO);
        } else {
            // 2.2.处理考试
            finished = handleExamRecord(userId, recordDTO);
        }
        if (!finished) {
            // 没有新学完的小节，无需更新课表中的学习进度
            return;
        }
        // 3.处理课表数据
        handleLearningLessonsChanges(recordDTO, userId);
    }

    private void handleLearningLessonsChanges(LearningRecordFormDTO recordDTO,Long userId) {
        // 1.查询课表
        LearningLesson lesson = lessonService.getById(recordDTO.getLessonId());
        if (lesson == null) {
            throw new BizIllegalException("课程不存在，无法更新数据！");
        }
        // 2.判断是否有新的完成小节
        boolean allLearned = false;

        // 3.如果有新完成的小节，则需要查询课程数据
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (cInfo == null) {
            throw new BizIllegalException("课程不存在，无法更新数据！");
        }
        // 4.比较课程是否全部学完：已学习小节 >= 课程总小节
        allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();

        // 5.更新课表
        lessonService.lambdaUpdate()
                .set(lesson.getLearnedSections() == 0, LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue())
                .set(allLearned, LearningLesson::getLatestLearnTime, LocalDateTime.now())
                .setSql("learned_sections = learned_sections + 1")
                .eq(LearningLesson::getId, lesson.getId())
                .update();
        //6.学习完一小节，利用mq异步向积分表发送消息，添加积分
        SignInMessage message = new SignInMessage();
        message.setUserId(userId);
        message.setPoints(10);
        mqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE, MqConstants.Key.LEARN_SECTION,message);
    }

    private boolean handleVideoRecord(Long userId, LearningRecordFormDTO recordDTO) {
        // 1.查询旧的学习记录
        LearningRecord old = queryOldRecord(recordDTO.getLessonId(), recordDTO.getSectionId());
        // 2.判断是否存在
        if (old == null) {
            // 3.不存在，则新增
            // 3.1.转换PO
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
            // 3.2.填充数据
            record.setUserId(userId);
            // 3.3.写入数据库
            boolean success = save(record);
            if (!success) {
                throw new DbException("新增学习记录失败！");
            }
            return false;
        }
        // 4.存在，则更新
        // 4.1.判断是否是第一次完成
        boolean finished = !old.getFinished() && recordDTO.getMoment() * 2 >= recordDTO.getDuration();
        if (!finished) {
            LearningRecord record = new LearningRecord();
            record.setLessonId(recordDTO.getLessonId());
            record.setSectionId(recordDTO.getSectionId());
            record.setMoment(recordDTO.getMoment());
            record.setId(old.getId());
            record.setFinished(old.getFinished());
            taskHandler.addLearningRecordTask(record);
            return false;
        }
        // 4.2.更新数据
        boolean success = lambdaUpdate()
                .set(LearningRecord::getMoment, recordDTO.getMoment())
                .set(LearningRecord::getFinished, true)
                .set(LearningRecord::getFinishTime, recordDTO.getCommitTime())
                .eq(LearningRecord::getId, old.getId())
                .update();
        if (!success) {
            throw new DbException("更新学习记录失败！");
        }
        // 4.3.清理缓存
        taskHandler.cleanRecordCache(recordDTO.getLessonId(), recordDTO.getSectionId());
        return true;
    }
    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        // 1.查询缓存
        LearningRecord record = taskHandler.readRecordCache(lessonId, sectionId);
        // 2.如果命中，直接返回
        if (record != null) {
            return record;
        }
        // 3.未命中，查询数据库
        record = lambdaQuery()
                .eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId)
                .one();
        // 4.写入缓存
        taskHandler.writeRecordCache(record);
        return record;
    }
    private boolean handleExamRecord(Long userId, LearningRecordFormDTO recordDTO) {
        // 1.转换DTO为PO
        LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
        // 2.填充数据
        record.setUserId(userId);
        record.setFinished(true);
        record.setFinishTime(recordDTO.getCommitTime());
        // 3.写入数据库
        boolean success = save(record);
        if (!success) {
            throw new DbException("新增考试记录失败！");
        }
        return true;
    }

    /**
    * 视频操作
    * */
    private boolean handleVideo(LearningRecordFormDTO learningRecordFormDTO, Long userId) {
        //1.调用课程记录表数据，判断是否第一次学习该视频
        /*LearningRecord record1 = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, learningRecordFormDTO.getLessonId())
                .eq(LearningRecord::getSectionId, learningRecordFormDTO.getSectionId())
                .one();*/
        //1.查询redis中的数据
        LearningRecord record1 = taskHandler.readRecordCache(learningRecordFormDTO.getLessonId(), learningRecordFormDTO.getSectionId());
        if (record1==null){
            //1.1查询数据库
            record1 = this.lambdaQuery()
                    .eq(LearningRecord::getLessonId, learningRecordFormDTO.getLessonId())
                    .eq(LearningRecord::getSectionId, learningRecordFormDTO.getSectionId())
                    .one();
        }
        if (record1==null){
            //课程表数据不存在，第一次观看该视频，直接新增数据
            LearningRecord record = BeanUtil.copyProperties(learningRecordFormDTO, LearningRecord.class);
            record.setUserId(userId);
            //此时第一次提交，没看完该视频
            record.setUpdateTime(learningRecordFormDTO.getCommitTime());
            boolean save = save(record);
            if (!save){
                throw new BizIllegalException("保存学习记录失败");
            }
            //返回false，表示该视频未学完
            return false;
        }
        //2.如果数据存在，将数据存到redis中
        taskHandler.writeRecordCache(record1);
        //数据存在，更新最近学习时间并且判断该视频是否学完：默认已学习时长*2>=总时长为学完
        //判断是否为第一次学完
        boolean isFinished = learningRecordFormDTO.getMoment()*2>=learningRecordFormDTO.getDuration() && !record1.getFinished();
        if (!isFinished){
            //将数据存到redis中
            LearningRecord record = new LearningRecord();
            record.setLessonId(learningRecordFormDTO.getLessonId());
            record.setSectionId(learningRecordFormDTO.getSectionId());
            record.setMoment(learningRecordFormDTO.getMoment());
            record.setId(record1.getId());
            record.setFinished(record1.getFinished());
            taskHandler.addLearningRecordTask(record);
            return false;
        }
        boolean update = this.lambdaUpdate()
                .set(LearningRecord::getMoment, learningRecordFormDTO.getMoment())//更新视频观看时长
                .set( LearningRecord::getFinished, true)//更新是否完成状态
                .set(LearningRecord::getFinishTime, learningRecordFormDTO.getCommitTime())//更新完成时间
                .eq(LearningRecord::getId, record1.getId())
                .update();
        if (!update){
            throw new BizIllegalException("更新学习记录失败");
        }
        //返回true，表示该视频学完,删除redis缓存
        taskHandler.cleanRecordCache(learningRecordFormDTO.getLessonId(), learningRecordFormDTO.getSectionId());
        return true;
    }


    /**
    * 考试操作
     * */
    private boolean handleExam(LearningRecordFormDTO learningRecordFormDTO, Long userId) {
        //插入学习记录表一条新数据
        LearningRecord record = BeanUtil.copyProperties(learningRecordFormDTO, LearningRecord.class);
        record.setUserId(userId);
        record.setFinished(true);
        record.setCreateTime(learningRecordFormDTO.getCommitTime());//创建时间
        record.setMoment(learningRecordFormDTO.getMoment());//提交时间
        record.setUpdateTime(learningRecordFormDTO.getCommitTime());//更新时间
        record.setFinishTime(learningRecordFormDTO.getCommitTime());//完成时间
        boolean save = save(record);
        if (!save){
            throw new BizIllegalException("保存学习记录失败");
        }
        return true;

    }
}



























